﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Script.Services;
using System.Web.UI;
using System.Web.UI.WebControls;
using GPC_Overlay;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using System.Threading.Tasks;
using System.Text;

namespace GISWebRole
{

    public partial class _Default : System.Web.UI.Page
    {

        private enum BalancingScheme { None, LargestFirst, RowOrder, ColumnOrder }

        private string _map1;
        private string _map2;
        private string _op;
        private GpcOperation _operator;
        public CloudStorageAccount _account;
        public string connectionString;
        public static int concurrencyLimit = 8;
        public static int currentOps = 0;
        public static int processed = 0;
        public static int toprocess = 0;
        public static List<string> blobs;
        public static List<Polygon> basePoly;
        public static List<Polygon> overlayPoly;

        int numMessages;
        bool processing = false;
      

        public TimeSpan fileDownloadTime = new TimeSpan(0, 0, 0); /* Time to download files from cloud storage */
        public TimeSpan parseTime = new TimeSpan(0, 0, 0); /* time to download files and parse them into C# objects */
        public TimeSpan graphCreation = new TimeSpan(0, 0, 0);/* Time to create intersection graph */
        public TimeSpan totalTaskCreationTime = new TimeSpan(0, 0, 0); /* Time to convert each task to XML and writing it to Cloud storage */
        public TimeSpan taskUploadingTime = new TimeSpan(0, 0, 0); /* Time to write tasks to cloud storage */
        public TimeSpan taskDownloadingTime = new TimeSpan(0, 0, 0); /* Time to download tasks for processing from cloud */
        public TimeSpan processingTime = new TimeSpan(0, 0, 0); /* time to download a task, convert to polygons, and process using third party library */
       

        protected void Page_Load(object sender, EventArgs e)
        {
            connectionString = RoleEnvironment.GetConfigurationSettingValue("DataConnectionString");
            _account = CloudStorageAccount.Parse(connectionString);
            if (!IsPostBack)
            {
                BlobHelper blobObject = new BlobHelper(connectionString);
                _setupDropDownLists(blobObject);
            }
        }

        private void _setupDropDownLists(BlobHelper blobObject)
        {

            CloudBlobContainer container = blobObject.BlobClient.GetContainerReference(DataConstants.MAP_CONTAINER_BLOB);
            CloudBlobDirectory dir = container.GetDirectoryReference("./");
            List<string> maps = (from map in dir.ListBlobs() select Path.GetFileName(map.Uri.AbsolutePath)).ToList();
            List<string> maps2 = (from map in maps select map).ToList();

            Array ops = Enum.GetValues(typeof(GpcOperation));

            Array schemes = Enum.GetValues(typeof(BalancingScheme));

            foreach (string filename in maps)
            {
                ddlMap1.Items.Add(filename);
                ddlMap2.Items.Add(filename);
            }
            foreach (object op in ops)
            {
                ddlOperator.Items.Add(op.ToString());
            }
        }

        public string Overlay(string map1, string map2, string op)
        {
            string rslt = _saveResult();
            System.Diagnostics.Trace.WriteLine("GISWebRole done overlay Job" + DateTime.Now.ToString(), "Information");
            System.Diagnostics.Trace.WriteLine("Result:" + rslt, "Information");
            return rslt;
        }

        public void assignWork()
        {
            string connectionString = RoleEnvironment.GetConfigurationSettingValue("DataConnectionString");
            BlobHelper blobObject = new BlobHelper(RoleEnvironment.GetConfigurationSettingValue("DataConnectionString"));
            QueueHelper QueueObject = new QueueHelper(RoleEnvironment.GetConfigurationSettingValue("DataConnectionString"));
            StringBuilder result = new StringBuilder();
            blobObject.DeleteBlob(DataConstants.MAP_CONTAINER_BLOB, "result.xml");
            /* Write the start of XML doc */
            result.AppendLine("<gml:FeatureCollection");
            result.AppendLine(" xmlns:gml=" + '"' + "http://www.opengis.net/gml" + '"');
            result.AppendLine(" xmlns:xlink=" + '"' + "http://www.w3.org/1999/xlink" + '"');
            result.AppendLine(" xmlns:xsi=" + '"' + "http://www.w3.org/2001/XMLSchema-instance" + '"');
            result.AppendLine(" xmlns:fme=" + '"' + "http://www.safe.com/gml/fme" + '"');
            result.AppendLine(" xsi:schemaLocation=" + '"' + "http://www.safe.com/gml/output.xsd>" + '"');
            BlobHelper.PutBlock(connectionString, DataConstants.MAP_CONTAINER_BLOB, DataConstants.OUTPUT_BLOB, Guid.NewGuid().ToString(), result.ToString());
            QueueObject.PutMessage(DataConstants.INPUT_PARAS, new CloudQueueMessage(_map1 + "{" + _map2 + "{" + _op));
        }

        public string checkProgress()
        {
            int finishMessages = 0;
           
            string rslt = string.Empty;
            CloudQueueMessage msg = new CloudQueueMessage("");
            QueueHelper queue = new QueueHelper(connectionString);

            if (numMessages == 0)
            {
                if (queue.PeekMessage(DataConstants.BLOB_COUNT, out msg))
                {
                    numMessages = int.Parse(msg.AsString);
                    rslt = "There are a total of " + numMessages + " features in the base layer.\n";
                }
            }

            queue.GetApproxMessageCount(DataConstants.RESULT_QUEUE, out finishMessages);
            if (finishMessages > 0)
            {
                rslt += finishMessages.ToString() + " out of " + numMessages.ToString() + " polygons have been processed.";

                if (finishMessages == numMessages)
                {
                    rslt = _saveResult();
                    processing = true;
                    rslt = "Crayons is done processing files.\n";
                }
            }
            else
            {
                rslt += "Crayons is distributing work among workers";
            }

            return rslt;
        }

        private string _saveResult()
        {
            string[] blockIDs;
            int numberOfWorkers = RoleEnvironment.Roles["GISWorkerRole"].Instances.Count;
            string connectionString = RoleEnvironment.GetConfigurationSettingValue("DataConnectionString");
            QueueHelper queue = new QueueHelper(connectionString);

            DateTime startSaveTime = DateTime.Now;
            // Write the end tag for the output ile 
            string outputEndTag = "</gml:FeatureCollection>";
            BlobHelper.PutBlock(connectionString, DataConstants.MAP_CONTAINER_BLOB, DataConstants.OUTPUT_BLOB, Guid.NewGuid().ToString(), outputEndTag);
            // Get the uncommitted blocks 
            BlobHelper.GetBlockList(connectionString, DataConstants.MAP_CONTAINER_BLOB, DataConstants.OUTPUT_BLOB, out blockIDs);
            //Commit the blob 
            BlobHelper.PutBlockList(connectionString, DataConstants.MAP_CONTAINER_BLOB, DataConstants.OUTPUT_BLOB, blockIDs);
            TimeSpan saveTime = DateTime.Now.Subtract(startSaveTime);
            List<CloudQueueMessage> msgs = new List<CloudQueueMessage>();
            IEnumerable<CloudQueueMessage> msg = new List<CloudQueueMessage>();

            CloudQueueMessage timeMsg = new CloudQueueMessage("");
            string strTimeMsg = String.Empty;
           
            int MessageCount = numMessages;

            /* Read all the messages. Tried parallelizing but itcrashes */
            while (msgs.Count < numMessages)
            {
                try
                {
                    MessageCount -= 32;
                    //We can retrieve a max of 32 messages 
                    MessageCount = Math.Min(32, MessageCount);
                    if (MessageCount > 0)
                    {
                        queue.GetMessage(DataConstants.RESULT_QUEUE, MessageCount, ref msg);
                        msgs.AddRange(msg);
                    }
                }
                catch (Exception e)
                {
                    System.Diagnostics.Trace.WriteLine("Error processing Results: " + e.Message + ":" + e.InnerException);
                }
            }

            DateTime msgTime = DateTime.Parse(msgs[0].AsString);
            foreach (CloudQueueMessage rslt_msg in msgs)
            {
                if (DateTime.Parse(rslt_msg.AsString) > msgTime)
                    msgTime = DateTime.Parse(rslt_msg.AsString);
            }

            System.Diagnostics.Trace.WriteLine("Processing ended at " + msgTime.ToString());

            queue.ClearMessages(DataConstants.RESULT_QUEUE);
            queue.ClearMessages(DataConstants.INPUT_PARAS);
            queue.ClearMessages(DataConstants.BLOB_COUNT);

            string rsltStr = "Output Creation time: " + (saveTime.TotalSeconds).ToString() + " Seconds\n";
            System.Diagnostics.Trace.WriteLine(rsltStr);

            return "Download resulting GML file from https://afcrayons.blob.core.windows.net/gpcfiles/result.xml";
        }

        protected void btnSubmit_Click(object sender, EventArgs e)
        {

            QueueHelper queue = new QueueHelper(connectionString);
            _op = ddlOperator.SelectedValue.ToString();
            _map1 = ddlMap1.SelectedValue.ToString();
            _map2 = ddlMap2.SelectedValue.ToString();

            if (_map1 == String.Empty || _map2 == string.Empty || _op == String.Empty)
            {
                txtTimer.Text = "All fields are required. Please select correct valuses and click submit again.";
            }
            else
            {
                assignWork();
                tmrUpdate.Enabled = true;
                btnSubmit.Visible = false;
                
            }
        }

        protected void ddlMap1_IndexChanged(object sender, EventArgs e)
        {
            _map1 = (ddlMap1.SelectedIndex != -1) ? ddlMap1.SelectedItem.ToString() : string.Empty;
        }

        protected void ddlOperator_IndexChanged(object sender, EventArgs e)
        {
            if (this.ddlOperator.SelectedIndex != -1)
            {
                _operator = (GpcOperation)Enum.Parse(typeof(GpcOperation), this.ddlOperator.SelectedItem.Value);
            }
            else
            {
                _operator = GpcOperation.Union;
            }
        }

        protected void ddlMap2_IndexChanged(object sender, EventArgs e)
        {
            _map2 = (ddlMap2.SelectedIndex != -1) ? ddlMap2.SelectedItem.ToString() : string.Empty;
        }

        protected void tmrUpdate_Tick(object sender, EventArgs e)
        {
            if (processing != true)
            {
                txtTimer.Text = checkProgress();
            }
            else
            {
                tmrUpdate.Enabled = false;
            }
        }

        protected void cmdReset_Click(object sender, EventArgs e)
        {
            txtTimer.Text = "";
            cmdReset.Visible = false;
            btnSubmit.Visible = true;
        }
    }
}

